/**
 * 
 */
package it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.filehandlers;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import it.unical.canonaco.rende.igpe.game.cbv.datastructure.ConstantSizeMap;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.SettingsBean;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.Character;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.items.Item;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.items.ItemFactory;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.stats.Role;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.stats.Stats;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.pawn.stats.StatsFactory;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.component.player.SimplePlayer;
import it.unical.canonaco.rende.igpe.game.cbv.logiclayer.engine.exceptions.StopParserExcpetion;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * @author Massimo Canonaco
 *
 */
public class PlayerFileHandler 
{
	/**It's the sax parser factory instance for getting the sax parser when it loads the player. */
	static private SAXParserFactory saxParserFactory;
	
	/**It's the used extension for the files. */
	static public final String EXTENSION = ".xml";
	
	/**It's the initial tag of the document. */
	static private final String ROOT_TAG = "root";
	
	/**It's the tag which contains the character's name. */
	static private final String CHARACTER_NAME = "name";
	
	/**It's the tag which contains the character's role */
	static private final String CHARACTER_ROLE = "role";
	
	/**It's the tag which contains the character's level. */
	static private final String CHARACTER_LEVEL = "level";
	
	/**It's the tag which contains the character's experience. */
	static private final String CHARACTER_EXPERIENCE = "experience";
	
	/**It's the tag which contains the character's items. */
	static private final String CHARACTER_ITEMS = "items";
	static private final String CHARACTER_ITEM_NAME = "item";
	
	/**It's the tag which indicates the character's informations will follow. */
	static private final String CHARACTER_TAG = "character";
	
	/** */
	private String profileFolderPath;
	
	private String itemsFilePath;
	
	private ItemFactory itmf = ItemFactory.getInstance();
	
	/** */
	public PlayerFileHandler()
	{
		if(saxParserFactory == null)
		{
			saxParserFactory = SAXParserFactory.newInstance();
		}
	}
	
	/** */
	public PlayerFileHandler(String profileFolder, String itemsFilePath)
	{
		init(profileFolder, itemsFilePath);
	}
	
	/** */
	public PlayerFileHandler(SettingsBean paths)
	{
		init(paths.getProfilesFolder(), paths.getItemsFile());
	}

	private void init(String profileFolder, String itemsFilePath) {
		if(saxParserFactory == null)
		{
			saxParserFactory = SAXParserFactory.newInstance();
		}
		
		this.profileFolderPath = profileFolder;
		this.itemsFilePath = itemsFilePath;
	}

	public void save(SimplePlayer player, boolean close)
	{
		save(player,profileFolderPath, close);
	}
	
	/** */
	public void save(SimplePlayer player, OutputStream outputStream, boolean close)
	{
		try 
		{
			writing(player, new PrintWriter(outputStream, true), close);
		} catch (IOException e) 
		{
			e.printStackTrace();
		}
	}
	
	/** */
	public void save(SimplePlayer player, PrintWriter printWriter, boolean close)
	{
		try 
		{
			BufferedWriter bw = new BufferedWriter(printWriter);
			bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
			writing(player, bw, close);
			bw.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	/** */
	private void writing(SimplePlayer player, final Writer writer, boolean close) throws IOException
	{
		final char startChar = '<', endChar = '>';
		final String slashChar = "/",
				startChararacterTag = startChar+CHARACTER_TAG+endChar,
				endCharacterTag = startChar+slashChar+CHARACTER_TAG+endChar,
				startNameTag = startChar+CHARACTER_NAME+endChar,
				endNameTag = startChar+slashChar+CHARACTER_NAME+endChar,
				starRoleTag = startChar+CHARACTER_ROLE+endChar,
				endRoleTag = startChar+slashChar+CHARACTER_ROLE+endChar,
				startLevelTag = startChar+CHARACTER_LEVEL+endChar,
				endLevelTag = startChar+slashChar+CHARACTER_LEVEL+endChar,
				startExperienceTag = startChar+CHARACTER_EXPERIENCE+endChar,
				endExperienceTag = startChar+slashChar+CHARACTER_EXPERIENCE+endChar,
				startItemsTag = startChar+CHARACTER_ITEMS+endChar,
				endItemsTag = startChar+slashChar+CHARACTER_ITEMS+endChar,
				startItemNameTag = startChar+CHARACTER_ITEM_NAME+endChar,
				endItemNameTag = startChar+slashChar+CHARACTER_ITEM_NAME+endChar;
		
		writer.write(startChar+ROOT_TAG+endChar);
//		writer.newLine();
		List<Character>characters = player.getCharacters();
		for (Character character : characters) 
		{
			writer.write(startChararacterTag);
//			writer.newLine();

			writer.write(startNameTag);
			writer.write(character.getName());
			writer.write(endNameTag);
//			writer.newLine();

			writer.write(starRoleTag);
			writer.write(character.getStats().getName());
			writer.write(endRoleTag);
//			writer.newLine();

			writer.write(startLevelTag);
			writer.write(String.valueOf(character.getLevel()));
			writer.write(endLevelTag);
//			writer.newLine();

			writer.write(startExperienceTag);
			writer.write(String.valueOf(character.getExperience()));
			writer.write(endExperienceTag);
//			writer.newLine();
			
			writer.write(startItemsTag);
//			writer.newLine();
			// get the name of the items of this character from its items map
			for(String itemName : character.getInventory().getItemNames()){
//				System.out.println(character.getName()+" - "+itemName);
				writer.write(startItemNameTag);
				writer.write(itemName);
				writer.write(endItemNameTag);
//				writer.newLine();
			}
			writer.write(endItemsTag);
//			writer.newLine();
			
			writer.write(endCharacterTag);
//			writer.newLine();
		}
		writer.write(startChar+slashChar+ROOT_TAG+endChar);
		
		if(close)
			writer.close();
	}
	/** */
	public File getPlayerFile(String name)
	{
		return new File(profileFolderPath+File.separatorChar+name+EXTENSION);
	}
	/** */
	public void save(SimplePlayer player, final String profileFolderPath, boolean close)
	{
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(profileFolderPath+File.separatorChar+player.getNickname()+EXTENSION));
			writing(player, writer, close);
//			writer.write(startChar+ROOT_TAG+endChar);
//			writer.newLine();
//			List<Character>characters = player.getCharacters();
//			for (Character character : characters) 
//			{
//				writer.write(startChararacterTag);
//				writer.newLine();
//
//				writer.write(startNameTag);
//				writer.write(character.getName());
//				writer.write(endNameTag);
//				writer.newLine();
//
//				writer.write(starRoleTag);
//				writer.write(character.getStats().getName());
//				writer.write(endRoleTag);
//				writer.newLine();
//
//				writer.write(startLevelTag);
//				writer.write(String.valueOf(character.getLevel()));
//				writer.write(endLevelTag);
//				writer.newLine();
//
//				writer.write(startExperienceTag);
//				writer.write(String.valueOf(character.getExperience()));
//				writer.write(endExperienceTag);
//				writer.newLine();
//				
//				writer.write(startItemsTag);
//				writer.newLine();
//				// get the name of the items of this character from its items map
//				for(String itemName : character.getInventory().getItemNames()){
////					System.out.println(character.getName()+" - "+itemName);
//					writer.write(startItemNameTag);
//					writer.write(itemName);
//					writer.write(endItemNameTag);
//					writer.newLine();
//				}
//				writer.write(endItemsTag);
//				writer.newLine();
//				
//				writer.write(endCharacterTag);
//				writer.newLine();
//			}
//			writer.write(startChar+slashChar+ROOT_TAG+endChar);
//			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void load(SimplePlayer player, String playerName,
			StatsFactory statsFactory, boolean load) {
		load(player,profileFolderPath, playerName, load);
		
		for(Character c  : player.getCharacters()){
			c.setStats(statsFactory.levellingRole(c.getStats().getName(), c.getLevel()));
			
			for(String itemName : c.getInventory().getItemNames())
				c.getStats().getCharacteristics().sum(itmf.getItem(itemName), true);
		}
		
	}
	public void load(SimplePlayer player, String folder, String name, StatsFactory statsFactory) {
		load(player,folder, name, true);
		
		for(Character c  : player.getCharacters()){
			c.setStats(statsFactory.levellingRole(c.getStats().getName(), c.getLevel()));
			
			for(String itemName : c.getInventory().getItemNames())
				c.getStats().getCharacteristics().sum(itmf.getItem(itemName), true);
		}
	}
	/** */
	public void load(SimplePlayer player, String playerName, boolean load)
	{
		load(player,profileFolderPath, playerName, load);
	}
	
	/** */
	public void load(SimplePlayer player, final String path, final String fileName, boolean load)
	{
		Reader saxHandler = new Reader();
		saxHandler.player = player;
		saxHandler.player.setNickname(fileName);
		try {
			saxParserFactory.newSAXParser().parse(new FileInputStream(path+File.separatorChar+fileName+PlayerFileHandler.EXTENSION), saxHandler);
		} catch (SAXException | IOException
				| ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	/** */
	public void load(SimplePlayer player, InputStream inputStream, boolean stop)
	{
		Reader saxHandler = new Reader();
		saxHandler.stop = stop;
		saxHandler.player = player;
		saxHandler.stamp = true;
//		saxHandler.player.setNickname("ANONYMOUSE ENEMY");
		try 
		{
//			new busaxParserFactory.newSAXParser().parse(new BufferedInputStream(inputStream), saxHandler);
			
			saxParserFactory.newSAXParser().parse(inputStream, saxHandler);
//			saxParserFactory.newSAXParser().parse(new BufferedInputStream(inputStream) , saxHandler);
		}
		catch(StopParserExcpetion e)
		{
			System.err.println("FINE DOCUMENTO CARICATO");
		}
		catch (SAXException | IOException | ParserConfigurationException e) 
		{
			e.printStackTrace();
		}
		System.out.println("end load");
	}
	


	/** */
	private class Reader extends DefaultHandler 
	{
		public boolean stamp;

		private boolean stop;

		/** */
		private boolean cName;

		/** */
		private boolean cRole;

		/** */
		private boolean cLevel;

		/** */
		private boolean cExperience;

		/** It is needed to load each character's item */
		private boolean cItemName;
		
		/** It's a flag to stop loading the list of items that character owns */
		private boolean cItems;
		
		/** */
		private Character underConstruction;

		/** */
		private SimplePlayer player;

		/** */
		public Reader()
		{
			cName = cRole = cLevel = cExperience = cItems = cItemName = false;
			stop = false;
			stamp = false;
		}

		/** */
		@Override
		public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

			if(stamp)
				System.out.println("."+qName+".");
			if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_TAG))
			{
				underConstruction = new Character(0, 0, player);
			}else if (underConstruction != null)
			{
				if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_NAME))
				{
					cName = true;
				}else if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_ROLE))
				{
					cRole = true;
				}else if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_LEVEL))
				{
					cLevel = true;
				}
				else if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_EXPERIENCE))
				{
					cExperience = true;
				}
				else if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_ITEMS))
					cItems = true;
				else if(cItems && qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_ITEM_NAME))
					cItemName = true;
			}
		}

		/** */
		@Override
		public void endElement(String uri, String localName, String qName) throws SAXException {
			if(stamp)
			System.out.println("!"+qName+"!");
			if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_TAG))
			{
				player.addCharacter(underConstruction);
				underConstruction = null;
			} else if(qName.equalsIgnoreCase(PlayerFileHandler.CHARACTER_ITEMS)){
					cItems = false;
			}
			else if(qName.equalsIgnoreCase(PlayerFileHandler.ROOT_TAG))
			{
				if(stop)
					throw new StopParserExcpetion();
			}
		}

		/** */
		@Override
		public void characters(char[] ch, int start, int length) throws SAXException {
			System.out.println("hey");
			if(stamp)
			System.out.println("["+new String(ch)+"]");
			if(cName)
			{
				underConstruction.setName(new String(ch, start, length));
				cName = false;
			}else if(cRole)
			{
				Role role = new Role();
				role.setName(new String(ch, start,length));
				underConstruction.setStats(role);
				cRole = false;
			}else if(cLevel)
			{
				underConstruction.setLevel(Integer.parseInt(new String(ch, start,length)));
				cLevel = false;
			}else if(cExperience)
			{
				underConstruction.setExperience(Integer.parseInt(new String(ch, start,length)));
				cExperience = false;
			}else if(cItemName){
			// When it encounters an item name, it uses ItemFactory (singleton) to load the item into a map from the player item file
				String tmp = new String(ch, start,length);
//				System.out.println(tmp+"pp");
				underConstruction.getInventory().addItem(tmp);
				cItemName = false;
			}
		}
	}

	/**
	 * @return the profileFolder
	 */
	public String getProfileFolder() {
		return profileFolderPath;
	}
}
